%%%----------------------------------------------------------------
%%% @author  H. Kerem Cevahir <hkerem@gmail.com>
%%% @doc
%%% @end
%%% @copyright 2009 H. Kerem Cevahir
%%%----------------------------------------------------------------
-module(cvd).

-include("tables.hrl").

-export([start/0]).

start() ->
	init_databases(),
	populate_databases().

init_databases() ->
	mnesia:create_schema([node()]),
	mnesia:start(),
	init_hdb_db(),
	init_ndb_db().

populate_databases() ->
	populate_hdb("/home/kerem/tmp/main.cvd/main.hdb"),
	populate_hdb("/home/kerem/tmp/daily.cvd/daily.hdb"),
	populate_ndb("/home/kerem/tmp/main.cvd/main.ndb"),
	populate_ndb("/home/kerem/tmp/daily.cvd/daily.ndb").

init_hdb_db() ->
	mnesia:create_table(hdb, [{attributes, record_info(fields, hdb)}]).

init_ndb_db() ->
	mnesia:create_table(ndb, [{attributes, record_info(fields, ndb)}]).

populate_hdb(Filename) ->
	for_each_line_in_file(Filename,
		fun(Line, _Count) ->
			[Digest, Size, Name] = string:tokens(Line, ":\n"),
			{SizeInt, _} = string:to_integer(Size),
			F = fun() -> mnesia:write(
					#hdb{digest=md5:hexstr_to_bin(Digest),
						size=SizeInt, name=Name}
				) end,
			mnesia:transaction(F) end,
		[read], 0).

populate_ndb(Filename) ->
	for_each_line_in_file(Filename,
		fun(Line, _Count) ->
			[Name, Type, Offset, Signature|Other] = string:tokens(Line, ":\n"),
			{TypeInt, _} = string:to_integer(Type),
			F = fun() -> mnesia:write(
					#ndb{name=Name, type=TypeInt,
						offset=parse_ndb_offset(Offset),
						signature=parse_ndb_signarture(Signature),
						other=Other}
				) end,
			mnesia:transaction(F) end,
		[read], 0).

parse_ndb_offset([$*]) ->
	any;
parse_ndb_offset([$E,$O,$F,$-,N]) ->
	{eof_minus, N - $0};
parse_ndb_offset([$E,$P,$+,N]) ->
	{entry_point_plus, N - $0};
parse_ndb_offset([$E,$P,$-,N]) ->
	{entry_point_minus, N - $0};
parse_ndb_offset([$S,$L,$+,N]) ->
	{last_section_plus, N - $0};
parse_ndb_offset([$S,$L,$-,N]) ->
	{last_section_minus, N - $0};
parse_ndb_offset([$S,X,$+,N]) ->
	{{section, X - $0}, {plus, N - $0}};
parse_ndb_offset([$S,X,$-,N]) ->
	{{section, X - $0}, {minus, N - $0}};
parse_ndb_offset(OffsetStr) when is_integer(OffsetStr) ->
	{OffsetInt, _} = string:to_integer(OffsetStr),
	OffsetInt.

parse_ndb_signarture(SignatureStr) ->
	ItmList1 = parse_ndb_signarture_grammer(SignatureStr),
	parse_ndb_signarture_organize_repeats(ItmList1).

parse_ndb_signarture_grammer(SignStr) ->
	parse_ndb_signarture_grammer([], SignStr).

parse_ndb_signarture_grammer(ParsedSig, [$*| SignStr]) ->
	parse_ndb_signarture_grammer([{repeat, {count, any_number}}|ParsedSig], SignStr);
parse_ndb_signarture_grammer(ParsedSig, [$( | SignStr]) ->
	{OrList, SignStr1} = parse_ndb_signarture_grammer_or(SignStr),
	parse_ndb_signarture_grammer([{or_list, OrList}|ParsedSig], SignStr1);
parse_ndb_signarture_grammer(ParsedSig, [${, N, $}| SignStr]) when is_integer(N) ->
	parse_ndb_signarture_grammer([{repeat, {count, N - $0}}|ParsedSig], SignStr);
parse_ndb_signarture_grammer(ParsedSig, [${, $-, N, $}| SignStr]) when is_integer(N) ->
	parse_ndb_signarture_grammer([{repeat, {count_or_less, N - $0}}|ParsedSig], SignStr);
parse_ndb_signarture_grammer(ParsedSig, [${, N, $-, $}| SignStr]) when is_integer(N) ->
	parse_ndb_signarture_grammer([{repeat, {count_or_more, N - $0}}|ParsedSig], SignStr);
parse_ndb_signarture_grammer(ParsedSig, [$?, $?| SignStr]) ->
	parse_ndb_signarture_grammer([any|ParsedSig], SignStr);
parse_ndb_signarture_grammer(ParsedSig, [$?, X| SignStr]) ->
	parse_ndb_signarture_grammer([{match_high, X}|ParsedSig], SignStr);
parse_ndb_signarture_grammer(ParsedSig, [X, $?| SignStr]) ->
	parse_ndb_signarture_grammer([{match_low, X}|ParsedSig], SignStr);
parse_ndb_signarture_grammer(ParsedSig, [X, Y| SignStr]) ->
	parse_ndb_signarture_grammer([md5:hex_to_int(X, Y)|ParsedSig], SignStr);
parse_ndb_signarture_grammer(ParsedSig, []) ->
	lists:reverse(ParsedSig).

parse_ndb_signarture_grammer_or(SignStr) ->
	parse_ndb_signarture_grammer_or([], SignStr).

parse_ndb_signarture_grammer_or(ParsedSigOr, [X, Y, $| | SignStr]) ->
	parse_ndb_signarture_grammer_or([md5:hex_to_int(X, Y)|ParsedSigOr], SignStr);
parse_ndb_signarture_grammer_or(ParsedSigOr, [X, Y, $) | SignStr]) ->
	{lists:reverse([md5:hex_to_int(X, Y)|ParsedSigOr]), SignStr}.

parse_ndb_signarture_organize_repeats(ParsedList) ->
	parse_ndb_signarture_organize_repeats([], ParsedList).

parse_ndb_signarture_organize_repeats(OrganizedList, [Item, {repeat, CountInfo} |ParsedList]) ->
	parse_ndb_signarture_organize_repeats([{repeat, {item, Item}, CountInfo}|OrganizedList], ParsedList);
parse_ndb_signarture_organize_repeats(OrganizedList, [Item |ParsedList]) ->
	parse_ndb_signarture_organize_repeats([Item|OrganizedList], ParsedList);
parse_ndb_signarture_organize_repeats(OrganizedList, []) ->
	lists:reverse(OrganizedList).

for_each_line_in_file(Name, Proc, Mode, Accum0) ->
    {ok, Device} = file:open(Name, Mode),
    for_each_line(Device, Proc, Accum0).

for_each_line(Device, Proc, Accum) ->
    case io:get_line(Device, "") of
        eof  -> 
		file:close(Device),
		Accum;
        Line -> 
		NewAccum = Proc(Line, Accum),
		for_each_line(Device, Proc, NewAccum)
    end.
